package cn.com.cifi.mars.service.impl;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.Map.Entry;
import java.util.stream.Collectors;

import cn.com.cifi.mars.bean.bo.returnCoefficient.ReturnCoefficientMySaleDataBO;
import cn.com.cifi.mars.bean.bo.returnCoefficient.ReturnCoefficientNosSaleDataBO;
import cn.com.cifi.mars.bean.dto.*;
import cn.com.cifi.mars.constant.*;
import cn.com.cifi.mars.entity.*;
import cn.com.cifi.mars.entity.common.SysProjectFetchNumber;
import cn.com.cifi.mars.service.*;
import cn.com.cifi.mars.util.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.MapUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import cn.com.cifi.mars.common.exception.MsgException;
import cn.com.cifi.mars.entity.common.IdmUser;
import cn.com.cifi.mars.mapper.mars.ProjectReturnCoefficientMapper;
import cn.com.cifi.mars.mapper.mars.ReturnCoefficientDetailMapper;
import cn.com.cifi.mars.mapper.mars.SignPlanVersionSupplyDetailMapper;
import cn.com.cifi.mars.mapper.wrapper.ProjectReturnCoefficientWrapper;
import cn.com.cifi.mars.mapper.wrapper.ReturnCoefficientDetailWrapper;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import javax.sql.DataSource;

/**
 * <p>
 * 回款系数详情表 服务实现类
 * </p>
 *
 * @author JingYuanXiang
 * @since 2020-11-05
 */
@Slf4j
@Service
public class ReturnCoefficientDetailServiceImpl extends ServiceImpl<ReturnCoefficientDetailMapper, ReturnCoefficientDetail> implements ReturnCoefficientDetailService {
    @Autowired
    private ReturnCoefficientDetailWrapper returnCoefficientDetailWrapper;
    @Autowired
    private ReturnCoefficientDetailMapper returnCoefficientDetailMapper;
    @Autowired
    private DmOdsMyVsMarsSContractService dmOdsMyVsMarsSContractService;
    @Autowired
    private SignPlanVersionSupplyDetailMapper signPlanVersionSupplyDetailMapper;
    @Autowired
    private VsMarsFeeService vsMarsFeeService;
    @Autowired
    private VsMarsGetinzclService vsMarsGetinzclService;
    @Autowired
    private SignPlanVersionService signPlanVersionService;
    @Autowired
    private ProjectReturnCoefficientMapper projectReturnCoefficientMapper;
    @Autowired
    private ProjectReturnCoefficientServiceImpl returnCoefficientService;
    @Autowired
    private DmOdsNosTradedataviewformarsService dmOdsNosTradedataviewformarsService;
    @Autowired
    private VsNosPayabledataviewformarsService vsNosPayabledataviewformarsService;
    @Autowired
    private ProjectReturnCoefficientWrapper projectReturnCoefficientWrapper;
    @Autowired
    private ProjectService projectService;
    @Autowired
    private SysProjectFetchNumberService projectFetchNumberService;
    @Autowired
    private MyConstants myConstants;
    @Autowired
    private DataSource dataSource;

    /**
     * 新建回款系数详情表
     *
     * @param coefficientId 回款系数版本id
     * @param user          用户
     */
    @Override
    public void saveDetail(String coefficientId, IdmUser user, String projectId) {
        //查询全盘签约计划版本列表
        List<String> singIdList = signPlanVersionService.getOverallSignVersionList(projectId);
        //不存在签约计划版本
        if (CollectionUtils.isEmpty(singIdList)) {
            throw new MsgException(CodeEnum.PRO_PRODUCT_CODE_BY_PROJECT_VERSION_SIGN_ERROR);
        }
        //获取标准业态下全部子类code集合
        Map<String, List<String>> standardCodeMap = getStandardCodeMap(singIdList);
        //项目调整板回款系数每次调整都基于项目最新实际版已生效标准业态、
        //(未生效的基于)项目最新投资正式版标准业态的回款系数进行调整;
        //回款系数详情表List
        List<ReturnCoefficientDetail> returnCoefficientDetails = new ArrayList<>();
        //获取全部版本
        List<ProjectReturnCoefficient> returnCoefficientList = projectReturnCoefficientMapper.queryByProjectId(projectId, IsDeleteEnum.NO.getKey());

        if (CollectionUtils.isNotEmpty(returnCoefficientList)) {
            //标准业态下回款系数详情表（key：标准业态，value：回款系数详情）
            Map<String, ReturnCoefficientDetail> map = new HashMap<>();
            
            //获取最新实际版
            ProjectReturnCoefficient returnCoefficient = projectReturnCoefficientWrapper.selectNewestByProjectId(projectId, ReturnVersionTypeEnum.PRACTICAL.getKey(), IsDeleteEnum.NO.getKey());
            if (returnCoefficient != null) {
                String id = returnCoefficient.getId();
                List<ReturnCoefficientDetail> list = returnCoefficientDetailWrapper.selectNewestByCoefficientIds(id, IsDeleteEnum.NO.getKey());
                if (CollectionUtils.isNotEmpty(list)) {
                    for (ReturnCoefficientDetail returnCoefficientDetail : list) {
                        LocalDateTime effectiveDate = returnCoefficientDetail.getEffectiveDate();
                        if (effectiveDate == null) {
                            continue;
                        }
                        String proProductCode = returnCoefficientDetail.getProProductCode();
                        if (effectiveDate.isBefore(LocalDateTime.now())) {
                            map.put(proProductCode, returnCoefficientDetail);
                        }
                    }
                }
            }

            //获取最新投资正式版标准业态的回款系数
            ProjectReturnCoefficient investOfficialReturnCoefficient = projectReturnCoefficientMapper.queryNewVersionByPojectId(projectId, IsDeleteEnum.NO.getKey(), ReturnVersionTypeEnum.INVEST_OFFICIAL.getKey());
            if (investOfficialReturnCoefficient != null) {
                String investOfficialId = investOfficialReturnCoefficient.getId();
                List<ReturnCoefficientDetail> investOfficialList = returnCoefficientDetailWrapper.selectNewestByCoefficientIds(investOfficialId, IsDeleteEnum.NO.getKey());
                if (MapUtils.isEmpty(map)) {
                    for (ReturnCoefficientDetail returnCoefficientDetail : investOfficialList) {
                        String proProductCode = returnCoefficientDetail.getProProductCode();
                        map.put(proProductCode, returnCoefficientDetail);
                    }
                }
                
                for (ReturnCoefficientDetail returnCoefficientDetail : investOfficialList) {
                    String proProductCode = returnCoefficientDetail.getProProductCode();
                    if (!map.containsKey(proProductCode)) {
                    	map.put(proProductCode, returnCoefficientDetail);
                    } 
                }
                
                /*Set<String> codes = map.keySet();
            	for (String code : codes) {
                    for (ReturnCoefficientDetail returnCoefficientDetail : investOfficialList) {
                        String proProductCode = returnCoefficientDetail.getProProductCode();
                        if (!code.equals(proProductCode)) {
                        	map.put(proProductCode, returnCoefficientDetail);
                        } 
                    }
                }*/
                
            }

            Set<String> codeSet = map.keySet();
        	for (String ProductCode : codeSet) {
                ReturnCoefficientDetail returnCoefficientDetail = map.get(ProductCode);
                returnCoefficientDetails.add(returnCoefficientDetail);
            }
            
            //不存在实际版和投资正式版
            if (CollectionUtils.isEmpty(returnCoefficientDetails)) {
                //key:项目下全部标准业态
                Set<String> codes = standardCodeMap.keySet();
                for (String standardCode : codes) {
                    initProductReturnCoefficientDetail(coefficientId, user, standardCode,null);
                }
            } else {
                //存在
                for (ReturnCoefficientDetail returnCoefficientDetail : returnCoefficientDetails) {
                	//标准业态编码
                    String standardCode = returnCoefficientDetail.getProProductCode();
                	initProductReturnCoefficientDetail(coefficientId, user, standardCode,returnCoefficientDetail);
                }
                //key:项目下全部标准业态编码
                Set<String> standardCodes = standardCodeMap.keySet();
                sign: for (String standardCode : standardCodes) {
                    //回款系数详情表已存在code
                    for (String code : codeSet) {
                        //如果存在不相同code 则新建版本数据为0
                        if (standardCode.equals(code)) {
                            continue sign;
                        }
                    }
                    initProductReturnCoefficientDetail(coefficientId, user, standardCode,null);
                }
            }
        } else {
            throw new MsgException(CodeEnum.PRO_PRODUCT_CODE_BY_PROJECT_VERSION_NOT_VERSION);
        }


    }

    /**
     * 
     * @Desc：初始化业态回款系数详情
     * @param coefficientId
     * @param user
     * @param standardCode void
     * @author：yyd
     * @date：2021年1月13日 下午5:15:51
     */
	private void initProductReturnCoefficientDetail(String coefficientId, IdmUser user, String standardCode,ReturnCoefficientDetail returnCoefficientDetail) {
		ReturnCoefficientDetail coefficientDetail = new ReturnCoefficientDetail();
		//回款系数详情表id
		coefficientDetail.setId(UUIDUtils.create());
		//回款系数版本表id
		coefficientDetail.setCoefficientId(coefficientId);
		//创建人员
		coefficientDetail.setCreateBy(user.getUsername());
		//创建日期
		coefficientDetail.setCreateTime(LocalDateTime.now());
		//是否删除
		coefficientDetail.setIsDelete(IsDeleteEnum.NO.getKey());
		//标准业态编码
		coefficientDetail.setProProductCode(standardCode);
		coefficientDetail.setProProductType(StandardFormatsEnum.getValueByKey(standardCode));
		//三大业态编码
		String threeStandardCode = StandardFormatsMapEnum.getValueByKey(standardCode);
		coefficientDetail.setThreeProductCode(threeStandardCode);
		coefficientDetail.setThreeProductName(StandardFormatsEnum.getValueByKey(threeStandardCode));

		BigDecimal defaultValue = BigDecimal.ZERO;
		coefficientDetail.setOneMonth(returnCoefficientDetail != null ? returnCoefficientDetail.getOneMonth() : defaultValue);
        coefficientDetail.setTwoMonth(returnCoefficientDetail != null ?returnCoefficientDetail.getTwoMonth() : defaultValue);
        coefficientDetail.setThreeMonth(returnCoefficientDetail != null ?returnCoefficientDetail.getThreeMonth(): defaultValue);
        coefficientDetail.setFourMonth(returnCoefficientDetail != null ?returnCoefficientDetail.getFourMonth(): defaultValue);
        coefficientDetail.setFiveMonth(returnCoefficientDetail != null ?returnCoefficientDetail.getFiveMonth(): defaultValue);
        coefficientDetail.setSixMonth(returnCoefficientDetail != null ?returnCoefficientDetail.getSixMonth(): defaultValue);
        coefficientDetail.setSevenMonth(returnCoefficientDetail != null ?returnCoefficientDetail.getSevenMonth(): defaultValue);
        coefficientDetail.setEightMonth(returnCoefficientDetail != null ?returnCoefficientDetail.getEightMonth(): defaultValue);
        coefficientDetail.setNineMonth(returnCoefficientDetail != null ?returnCoefficientDetail.getNineMonth(): defaultValue);
        coefficientDetail.setTenMonth(returnCoefficientDetail != null ?returnCoefficientDetail.getTenMonth(): defaultValue);
        coefficientDetail.setElevenMonth(returnCoefficientDetail != null ?returnCoefficientDetail.getElevenMonth(): defaultValue);
        coefficientDetail.setTwelveMonth(returnCoefficientDetail != null ?returnCoefficientDetail.getTwelveMonth(): defaultValue);
        coefficientDetail.setThirteenMonth(returnCoefficientDetail != null ?returnCoefficientDetail.getThirteenMonth(): defaultValue);
        coefficientDetail.setFourteenMonth(returnCoefficientDetail != null ?returnCoefficientDetail.getFourteenMonth(): defaultValue);
        coefficientDetail.setFifteenMonth(returnCoefficientDetail != null ?returnCoefficientDetail.getFifteenMonth(): defaultValue);
        coefficientDetail.setSixteenMonth(returnCoefficientDetail != null ?returnCoefficientDetail.getSixteenMonth(): defaultValue);
        coefficientDetail.setSeventeenMonth(returnCoefficientDetail != null ?returnCoefficientDetail.getSeventeenMonth(): defaultValue);
        coefficientDetail.setEighteenMonth(returnCoefficientDetail != null ?returnCoefficientDetail.getEighteenMonth(): defaultValue);
        coefficientDetail.setNineteenMonth(returnCoefficientDetail != null ?returnCoefficientDetail.getNineteenMonth(): defaultValue);
        coefficientDetail.setTwentyMonth(returnCoefficientDetail != null ?returnCoefficientDetail.getTwentyMonth(): defaultValue);
        coefficientDetail.setTwentyOneMonth(returnCoefficientDetail != null ?returnCoefficientDetail.getTwentyOneMonth(): defaultValue);
        coefficientDetail.setTwentyTwoMonth(returnCoefficientDetail != null ?returnCoefficientDetail.getTwentyTwoMonth(): defaultValue);
        coefficientDetail.setTwentyThreeMonth(returnCoefficientDetail != null ?returnCoefficientDetail.getTwentyThreeMonth(): defaultValue);
        coefficientDetail.setTwentyFourMonth(returnCoefficientDetail != null ?returnCoefficientDetail.getTwentyFourMonth(): defaultValue);
		this.save(coefficientDetail);
	}
	
    /**
     * 获取回款系数版本版本详情
     *
     * @param coefficientId 版本id
     */
    @Override
    public List<ReturnCoefficientDetail> queryByCoefficientId(String coefficientId) {
        List<ReturnCoefficientDetail> list = returnCoefficientDetailWrapper.selectByCoefficientId(coefficientId, IsDeleteEnum.NO.getKey());
        if (CollectionUtils.isEmpty(list)) {
            throw new MsgException(CodeEnum.NON_VERSIONS);
        }
        return list;
    }

    /**
     * 生成项目实际版回款系数
     *
     * @param projectId 项目id
     */
    @Transactional
    public void createPracticalByProjectId(String projectId) {
        //获取容差金额
        BigDecimal tolerance = myConstants.getTolerance(SysDictionaryTypeEnum.TOLERACE.getKey());
        //获取主数据项目数据
        MdmProject project = projectService.selectByProId(projectId);
        //获取最新项目实际版回款系数详情列表
        List<ReturnCoefficientDetail> returnCoefficientDetailList = returnCoefficientDetailMapper.selectLastTimeGroupByProjectId(projectId,ReturnVersionTypeEnum.PRACTICAL.getKey(), IsDeleteEnum.NO.getKey());
        //获取最新项目回款系数详情映射
        Map<String, List<ReturnCoefficientDetail>> formerProjectDetailListMap = new HashMap<>();
        if(CollectionUtils.isNotEmpty(returnCoefficientDetailList)){
            formerProjectDetailListMap = returnCoefficientDetailList.stream().collect(Collectors.groupingBy(s ->s.getProjectId()));
        }
        //根据项目id获取全盘签约id列表
        List<String> signVersionIdList = signPlanVersionService.getOverallSignVersionList(project.getProjectId());
        if (CollectionUtils.isNotEmpty(signVersionIdList)) {
            //获取全盘供货详情列表
            List<SignPlanVersionSupplyDetail> supplyDetailList = signPlanVersionSupplyDetailMapper.selectByVersionIdListAndType(signVersionIdList, OrgTypeEunm.ORG_PRODUCT.getKey(), IsDeleteEnum.NO.getKey());
            //生成实际版回款系数
            createPractical(project,supplyDetailList,tolerance, formerProjectDetailListMap.get(project.getProjectId()));
        }
    }

    /**
     * 生成全部回款系数
     */
    @Transactional
    @Override
    public void createAllPractical() {
        //获取容差金额
        BigDecimal tolerance = myConstants.getTolerance(SysDictionaryTypeEnum.TOLERACE.getKey());
        //获取项目id列表
        List<MdmProject> projectList = projectService.selectAll();
        //查询全盘最新签约计划版本id列表映射
        Map<String, List<String>> signVersionListMap = signPlanVersionService.getOverallSignVersionListMap();
        //获取全盘供货详情列表映射
        Map<String, List<SignPlanVersionSupplyDetail>> supplyDetailListMap = getSupplyDetailListMap(signVersionListMap);
        //获取最新项目实际版回款系数详情列表
        List<ReturnCoefficientDetail> returnCoefficientDetailList = returnCoefficientDetailMapper.selectLastTimeGroupByProjectId(null,ReturnVersionTypeEnum.PRACTICAL.getKey(), IsDeleteEnum.NO.getKey());
        //获取最新项目回款系数详情映射
        Map<String, List<ReturnCoefficientDetail>> formerProjectDetailListMap = new HashMap<>();
        if(CollectionUtils.isNotEmpty(returnCoefficientDetailList)){
            formerProjectDetailListMap = returnCoefficientDetailList.stream().collect(Collectors.groupingBy(s ->s.getProjectId()));
        }
        List<String> resultList = new ArrayList<>();
        for (MdmProject project : projectList) {
            //获取项目全盘签约id列表
            List<String> signVersionIdList = signVersionListMap.get(project.getProjectId());
            //获取项目全盘供货详情列表
            List<SignPlanVersionSupplyDetail> supplyDetailList = supplyDetailListMap.get(project.getProjectId());
            if (CollectionUtils.isNotEmpty(signVersionIdList) && CollectionUtils.isNotEmpty(supplyDetailList)) {
                //手动开启事务
                DataSourceTransactionManager dSTransactionManager = new DataSourceTransactionManager();
                dSTransactionManager.setDataSource(dataSource);
                DefaultTransactionDefinition dtDefinition = new DefaultTransactionDefinition();
                //新发起一个事务
                dtDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
                //获取事务状态
                TransactionStatus transactionStatus = dSTransactionManager.getTransaction(dtDefinition);

                try {
                    //生成实际版回款系数
                    createPractical(project,supplyDetailList,tolerance,formerProjectDetailListMap.get(project.getProjectId()));

                    //逻辑代码，可以写上你的逻辑处理代码
                    dSTransactionManager.commit(transactionStatus);
                }catch (MsgException e){
                    dSTransactionManager.rollback(transactionStatus);
                    resultList.add("项目id："+ project.getProjectId() + "项目名称："+ project.getProjectName() +","+ e.getMsg());
                }
                catch (Exception e){
                    dSTransactionManager.rollback(transactionStatus);
                    log.error(e.getLocalizedMessage());
                    SysLogUtils.addSysLog(LogEventEnum.REFRESH,"回款系数","生成实际版回款系数","项目id:"+project.getProjectId()+",项目名称："+ project.getProjectName() +",错误原因："+e.getLocalizedMessage());
                }
            }else {
                resultList.add("项目id："+ project.getProjectId() + "项目名称："+ project.getProjectName() +",无全盘签约版本");
            }
        }
        SysLogUtils.addSysLog(LogEventEnum.ADD, "回款系数", "自动生成实际版回款系数", resultList.toString());
    }

    /**
     * 获取全盘供货详情列表映射
     * @param signVersionListMap
     * @return
     */
    @Override
    public Map<String, List<SignPlanVersionSupplyDetail>> getSupplyDetailListMap(Map<String, List<String>> signVersionListMap) {
        //获取签约id月项目id的映射关系
        Map<String, String> projectIdMap = signVersionListMap.entrySet().stream()
                .map(s -> s.getValue().stream().collect(Collectors.toMap(s1 -> s1, s1 -> s.getKey())))
                .flatMap(m -> m.entrySet().stream())
                .collect(Collectors.toMap(Entry::getKey, Entry::getValue));

        //获取供货详情列表
        List<SignPlanVersionSupplyDetail> supplyDetailList = signPlanVersionSupplyDetailMapper.selectByVersionIdListAndType(new ArrayList(projectIdMap.keySet()), OrgTypeEunm.ORG_PRODUCT.getKey(), IsDeleteEnum.NO.getKey());
        //以项目将数据分组
        Map<String, List<SignPlanVersionSupplyDetail>> supplyDetailListMap = supplyDetailList.stream().collect(Collectors.groupingBy(s -> projectIdMap.get(s.getVersionId())));
        return supplyDetailListMap;
    }

    /**
     * 生成实际版回款系数
     * @param signVersionIdList
     * @param project 项目数据
     * @param supplyDetailList 项目供货详情数据列表
     * @param tolerance 容错
     * @param formerProjectDetailList 上一版回款系数详情列表
     */
    @Transactional
    public void createPractical(MdmProject project, List<SignPlanVersionSupplyDetail> supplyDetailList, BigDecimal tolerance, List<ReturnCoefficientDetail> formerProjectDetailList) {
        //获取楼栋id列表
        List<String> buildList = supplyDetailList.stream().map(SignPlanVersionSupplyDetail::getOrgId).collect(Collectors.toList());
        //获取项目销售取数系统
        String fetchSys = getProjectSaleFetchSys(project.getProjectId());
        if (FetchNnumberEnum.MY.getKey().equals(fetchSys)) {
            //获取合同列表
            List<MymContractDto> contractList = dmOdsMyVsMarsSContractService.selectMymContractListOfActivate(buildList);
            if (CollectionUtils.isEmpty(contractList)) {
                throw new MsgException(CodeEnum.CREATE_COEFFICIENT_CONTRACT_IS_NULL_ERROR);
            }
            //获取应收列表
            List<VsMarsFeeDto> receivableList = vsMarsFeeService.selectByProjectId(project.getProjectId());
            //获取实收列表
            List<VsMarsGetinzcl> receiptsList = vsMarsGetinzclService.selectByKingdeeProjectId(project.getKingdeeProjectId());

            //回款系数明源销售相关数据初始化
            ReturnCoefficientMySaleDataBO mySaleDataBO = new ReturnCoefficientMySaleDataBO();
            mySaleDataBO.setSupplyDetailList(supplyDetailList);
            mySaleDataBO.setContractList(contractList);
            mySaleDataBO.setReceivableList(receivableList);
            mySaleDataBO.setReceiptsList(receiptsList);
            mySaleDataBO.setTolerance(tolerance);
            mySaleDataBO.initializeMap();

            //获取合同id列表Map（key：标准业态编码，value：合同id列表）
            Map<String, List<String>> contractGUIDListByProProductCodeMap = mySaleDataBO.getContractGUIDListByProProductCodeMap();
            //获取标准业态的回款金额Map(key:标准业态编码:value:回款金额)
            Map<String, BigDecimal> proProductReturnAmountMap = getProProductReturnAmountMap(contractGUIDListByProProductCodeMap,mySaleDataBO);
            //获取合同月数回款金额Map(key:合同id:value:(key:月数,value:回款金额))
            Map<String, Map<Integer, BigDecimal>> contractMonthAmountMap = getContractMonthAmountMap(mySaleDataBO);
            //获取标准业态月数回款金额Map(key:标准业态编码:value:(key:月数,value:回款金额))
            Map<String, Map<Integer, BigDecimal>> proProductMonthAmountMap = getProProductMonthAmountMap(contractMonthAmountMap,contractGUIDListByProProductCodeMap);
            //获取标准业态月数回款系数Map(key:标准业态编码:value:(key:月数,value:系数))
            Map<String, Map<Integer, BigDecimal>> proProductMonthCoefficientMap = getProProductMonthCoefficientMap(proProductMonthAmountMap);
            //获取标准业态回款率Map(key:标准业态，value：累计签约回款)
            Map<String, BigDecimal> proProductReturmRate = mySaleDataBO.getProProductReturmRateMap();
            //获取标准业态首次签约日期Map(key:标准业态编码，value：首次签约日期)
            Map<String, LocalDateTime> proProductSignFirstTimeMap = mySaleDataBO.getProProductSignFirstTimeMap(contractGUIDListByProProductCodeMap,mySaleDataBO);
            //获取标准业态套数去化率Map(key:标准业态，value：套数去化率)
            Map<String, BigDecimal> productSoldNumRateMap = getProProductSoldNumRateMap(mySaleDataBO);
            //生效日期(key：标准业态编码，value：生效日期)
            Map<String, LocalDateTime> effectiveDates = getEffectiveDatesMap(proProductSignFirstTimeMap, productSoldNumRateMap,formerProjectDetailList);

            //新建版本表
            ProjectReturnCoefficient coefficient = returnCoefficientService.createReality(project.getProjectId());
            //新建详情表
            saveRealityDetail(coefficient.getId(), proProductMonthCoefficientMap, productSoldNumRateMap, effectiveDates, proProductReturmRate, proProductSignFirstTimeMap);
        }else if(FetchNnumberEnum.NOS.getKey().equals(fetchSys)){
            //获取合同列表
            List<NosTradedataDto> roomList = dmOdsNosTradedataviewformarsService.selectNosContractList(buildList);
            if (CollectionUtils.isEmpty(roomList)) {
                throw new MsgException(CodeEnum.CREATE_COEFFICIENT_CONTRACT_IS_NULL_ERROR);
            }
            //获取实收列表
            List<VsNosGetin> receiptsList = vsNosPayabledataviewformarsService.selectByProjectId(project.getProjectId());

            //回款系数NOS销售相关数据初始化
            ReturnCoefficientNosSaleDataBO nosSaleDataBO = new ReturnCoefficientNosSaleDataBO();
            nosSaleDataBO.setSupplyDetailList(supplyDetailList);
            nosSaleDataBO.setRoomList(roomList);
            nosSaleDataBO.setReceiptsList(receiptsList);
            nosSaleDataBO.setTolerance(tolerance);
            nosSaleDataBO.initializeMap();

            //获取房间id列表Map（key：标准业态编码，value：房间id列表）
            Map<String, List<String>> roomIdListByProProductCodeMap = nosSaleDataBO.getTakeBackRoomIdListByProProductCodeMap();
            //获取标准业态的回款金额Map(key:标准业态编码:value:回款金额)
            Map<String, BigDecimal> proProductReturnAmountMap = getProProductReturnAmountMap(roomIdListByProProductCodeMap,nosSaleDataBO);
            //获取房间月数回款金额Map(key:房间id:value:(key:月数,value:回款金额))
            Map<String, Map<Integer, BigDecimal>> roomMonthAmountMap = getRoomMonthAmountMap(nosSaleDataBO);
            //获取标准业态月数回款金额Map(key:标准业态编码:value:(key:月数,value:回款金额))
            Map<String, Map<Integer, BigDecimal>> proProductMonthAmountMap = getProProductMonthAmountMap(roomMonthAmountMap,roomIdListByProProductCodeMap);
            //获取标准业态月数回款系数Map(key:标准业态编码:value:(key:月数,value:系数))
            Map<String, Map<Integer, BigDecimal>> proProductMonthCoefficientMap = getProProductMonthCoefficientMap(proProductMonthAmountMap);
            //获取标准业态回款率Map(key:标准业态，value：累计签约回款)
            Map<String, BigDecimal> proProductReturmRate = nosSaleDataBO.getProProductReturmRateMap();
            //获取标准业态首次签约日期Map(key:标准业态编码，value：首次签约日期)
            Map<String, LocalDateTime> proProductSignFirstTimeMap = getProProductSignFirstTimeMap(roomIdListByProProductCodeMap,nosSaleDataBO);
            //获取标准业态套数去化率Map(key:标准业态，value：套数去化率)
            Map<String, BigDecimal> productSoldNumRateMap = getProProductSoldNumRateMap(nosSaleDataBO);
            //生效日期(key：标准业态编码，value：生效日期)
            Map<String, LocalDateTime> effectiveDates = getEffectiveDatesMap(proProductSignFirstTimeMap, productSoldNumRateMap, formerProjectDetailList);

            //新建版本表
            ProjectReturnCoefficient coefficient = returnCoefficientService.createReality(project.getProjectId());
            //新建详情表
            saveRealityDetail(coefficient.getId(), proProductMonthCoefficientMap, productSoldNumRateMap, effectiveDates, proProductReturmRate, proProductSignFirstTimeMap);
        }
    }

    /**
     * 获取标准业态套数去化率Map(key:标准业态，value：套数去化率)
     * @param nosSaleDataBO
     * @return
     */
    private Map<String, BigDecimal> getProProductSoldNumRateMap(ReturnCoefficientNosSaleDataBO nosSaleDataBO) {
        //获取标准业态已售套数Map(key:标准业态编码,value:已售套数)
        Map<String,BigDecimal> proProductSoldNumMap = nosSaleDataBO.getProProductSoldNumMap();
        //获取标准业态供货套数Map(key:标准业态编码,value:供货套数)
        Map<String,BigDecimal> proProductSupplyNumMap =nosSaleDataBO.getProProductSupplyNumMap();
        //获取标准业态套数去化率Map(key:标准业态，value：套数去化率)
        Map<String, BigDecimal> productSoldNumRateMap = proProductSupplyNumMap.entrySet().stream()
                .collect(Collectors.toMap(s -> s.getKey(), s -> BigDecimalUtils.divBigDecimal(proProductSoldNumMap.get(s.getKey()), s.getValue(), 5)));
        return productSoldNumRateMap;
    }


    /**
     * 获取标准业态套数去化率Map(key:标准业态，value：套数去化率)
     * @param mySaleDataBO
     * @return
     */
    private Map<String, BigDecimal> getProProductSoldNumRateMap(ReturnCoefficientMySaleDataBO mySaleDataBO) {
        //获取标准业态已售套数Map(key:标准业态编码,value:已售套数)
        Map<String,BigDecimal> proProductSoldNumMap = mySaleDataBO.getProProductSoldNumMap();
        //获取标准业态供货套数Map(key:标准业态编码,value:供货套数)
        Map<String,BigDecimal> proProductSupplyNumMap =mySaleDataBO.getProProductSupplyNumMap();
        //获取标准业态套数去化率Map(key:标准业态，value：套数去化率)
        Map<String, BigDecimal> productSoldNumRateMap = proProductSupplyNumMap.entrySet().stream()
                .collect(Collectors.toMap(s -> s.getKey(), s -> BigDecimalUtils.divBigDecimal(proProductSoldNumMap.get(s.getKey()), s.getValue(), 5)));
        return productSoldNumRateMap;
    }


    /**
     * 获取标准业态首次签约日期Map(key:标准业态编码，value：首次签约日期)
     * @param contractGUIDListMap
     * @param nosSaleDataBO
     * @return
     */
    private Map<String, LocalDateTime> getProProductSignFirstTimeMap(Map<String, List<String>> contractGUIDListMap, ReturnCoefficientNosSaleDataBO nosSaleDataBO) {
        //获取房间签约时间Map(key:房间id,value:签约时间)
        Map<String,LocalDateTime> tradeDateMap = nosSaleDataBO.getRoomTradeDateMap();

        Map<String, LocalDateTime> proProductSignFirstTimeMap = contractGUIDListMap.entrySet().stream().collect(Collectors.toMap(s -> s.getKey(),
                s -> s.getValue().stream().map(s1 -> tradeDateMap.get(s1)).min(Comparator.comparing(s1 -> s1)).get()));
        return proProductSignFirstTimeMap;
    }

    /**
     * 获取标准业态的回款金额Map
     * @param contractGUIDListMap
     * @param nosSaleDataBO
     * @return
     */
    private Map<String, BigDecimal> getProProductReturnAmountMap(Map<String, List<String>> contractGUIDListMap, ReturnCoefficientNosSaleDataBO nosSaleDataBO) {
        Map<String, BigDecimal> proProductAmountMap = contractGUIDListMap.entrySet().stream().collect(Collectors.toMap(s -> s.getKey(),
                s -> s.getValue().stream().map(s1-> nosSaleDataBO.getReceiptsAmountByRoomId(s1)).reduce(BigDecimal::add).orElse(BigDecimal.ZERO)));
        return proProductAmountMap;

    }

    /**
     * 获取标准业态的回款金额Map
     * @param contractGUIDListMap
     * @param mySaleDataBO
     * @return
     */
    private Map<String, BigDecimal> getProProductReturnAmountMap(Map<String, List<String>> contractGUIDListMap, ReturnCoefficientMySaleDataBO mySaleDataBO) {
        Map<String, BigDecimal> proProductAmountMap = contractGUIDListMap.entrySet().stream().collect(Collectors.toMap(s -> s.getKey(),
                        s -> s.getValue().stream().map(s1-> mySaleDataBO.getReceiptsAmountByContractGUID(s1)).reduce(BigDecimal::add).orElse(BigDecimal.ZERO)));
        return proProductAmountMap;

    }

    /**
     * 标准业态月数回款系数Map(key:标准业态编码:value:(key:月数,value:系数))
     * @param proProductMonthAmountMap
     * @return
     */
    @Override
    public Map<String, Map<Integer, BigDecimal>> getProProductMonthCoefficientMap(Map<String, Map<Integer, BigDecimal>> proProductMonthAmountMap) {
        //获取标准业态的总金额Map(key:标准业态编码:value:回款金额)
        Map<String, BigDecimal> proProductAmountMap = proProductMonthAmountMap.entrySet().stream()
                .collect(Collectors.toMap(s -> s.getKey(), s -> s.getValue().entrySet().stream().map(s1 -> s1.getValue()).reduce(BigDecimal::add).orElse(BigDecimal.ZERO)));

        Map<String, Map<Integer, BigDecimal>> proProductMonthCoefficientMap = proProductMonthAmountMap.entrySet().stream()
                .collect(Collectors.toMap(s -> s.getKey(), s -> s.getValue().entrySet().stream()
                        .collect(Collectors.toMap(s1 -> s1.getKey(), s1 -> BigDecimalUtils.divBigDecimal(s1.getValue(), proProductAmountMap.get(s.getKey()), 5)))));

        return proProductMonthCoefficientMap;
    }

    /**
     * 获取标准业态月数回款金额Map
     * @param contractMonthAmountMap
     * @param contractGUIDListMap
     * @return
     */
    @Override
    public Map<String, Map<Integer, BigDecimal>> getProProductMonthAmountMap(Map<String, Map<Integer, BigDecimal>> contractMonthAmountMap, Map<String, List<String>> contractGUIDListMap) {

        Map<String, Map<Integer, BigDecimal>> proProductAmountMap = contractGUIDListMap.entrySet().stream().collect(Collectors.toMap(s -> s.getKey(), s -> {
            Map<Integer, BigDecimal> map = MyMapUtils.getMoneyByMonthMap();

            List<String> contractGUIDList = s.getValue();
            for (String contractGUID : contractGUIDList) {
                Map<Integer, BigDecimal> subMap = contractMonthAmountMap.get(contractGUID);
                if(CollectionUtils.isNotEmpty(subMap)){
                    for (Entry<Integer, BigDecimal> entry : subMap.entrySet()) {
                        map.put(entry.getKey(), map.get(entry.getKey()).add(entry.getValue()));
                    }
                }
            }
            return map;
        }));
        return proProductAmountMap;
    }

    /**
     * 获取房间月数回款金额Map
     * @param nosSaleDataBO
     * @return
     */
    @Override
    public Map<String, Map<Integer, BigDecimal>> getRoomMonthAmountMap(ReturnCoefficientNosSaleDataBO nosSaleDataBO) {
        //获取房间金额映射
        Map<String, BigDecimal> roomAmountByRoomIdMap = nosSaleDataBO.getRoomAmountMap();
        //获取房间签约时间映射
        Map<String, LocalDateTime> roomTradeDateMap = nosSaleDataBO.getRoomTradeDateMap();
        //容差金额
        BigDecimal tolerance = nosSaleDataBO.getTolerance();

        HashMap<String, Map<Integer, BigDecimal>> roomAmountMap = new HashMap<>();
        for (Entry<String, BigDecimal> roomEntry : roomAmountByRoomIdMap.entrySet()) {
            //房间签约回款场景及场景对应的房源第N个月回款金额计算map(key；24月，value：每月回款金额)
            Map<Integer, BigDecimal> amountMap = MyMapUtils.getMoneyByMonthMap();

            //房间id
            String roomId = roomEntry.getKey();
            //房间成交金额
            BigDecimal tradeMoney = roomEntry.getValue();
            //房间成交日期
            LocalDateTime tradeDate = roomTradeDateMap.get(roomId);
            //获取合同实收金额
            BigDecimal receiptsAmountTotal = nosSaleDataBO.getReceiptsAmountByRoomId(roomId);

            //获取合同实收金额Map
            Map<String, BigDecimal> roomReceiptsAmountMap = nosSaleDataBO.getRoomReceiptsAmountMapByRoomId(roomId);
            if(CollectionUtils.isNotEmpty(roomReceiptsAmountMap)){
                //历史月已实收款计算回款金额
                for (Entry<String, BigDecimal> entry : roomReceiptsAmountMap.entrySet()){
                    int diff = LocalDateTimeUtils.monthDiff(tradeDate, LocalDateTimeUtils.toLocalDateTime(entry.getKey()));
                    if (diff <= 0){
                        amountMap.put(1,amountMap.get(1).add(entry.getValue()));
                    } else if (diff >= 24){
                        amountMap.put(24,amountMap.get(24).add(entry.getValue()));
                    } else {
                        amountMap.put(diff+1,amountMap.get(diff+1).add(entry.getValue()));
                    }
                }
            }

            //实收总额不等于合同金额（将差异金额补到当前月上）
            if (receiptsAmountTotal.subtract(tradeMoney).abs().compareTo(tolerance) > 0) {
                BigDecimal differenceMoney = tradeMoney.subtract(receiptsAmountTotal);
                int diff = LocalDateTimeUtils.monthDiff(tradeDate, LocalDateTime.now());
                if (diff <= 0){
                    amountMap.put(1,amountMap.get(1).add(differenceMoney));
                } else if (diff >= 24){
                    amountMap.put(24,amountMap.get(24).add(differenceMoney));
                } else {
                    amountMap.put(diff+1,amountMap.get(diff+1).add(differenceMoney));
                }
            }
            roomAmountMap.put(roomId, amountMap);
        }
        return roomAmountMap;

    }

    /**
     * 获取合同月数回款金额Map
     * @param mySaleDataBO
     * @return
     */
    @Override
    public HashMap<String, Map<Integer, BigDecimal>> getContractMonthAmountMap(ReturnCoefficientMySaleDataBO mySaleDataBO) {
        List<String> list = Arrays.asList("IX+14BifSoahXtcr6zk3OoA+blo=", "1Txp8myNSVGNy2tqUfLddoA+blo=", "t9uMZ2YvTFKavYsxQzQ1nIA+blo=");
        //合同列表
        List<MymContractDto> contractList = mySaleDataBO.getContractList();
        //容差金额
        BigDecimal tolerance = mySaleDataBO.getTolerance();
        //获取当前年月
        String currentYearMonth = LocalDateTimeUtils.getLocalMonth(LocalDateTime.now());

        HashMap<String, Map<Integer, BigDecimal>> contractAmountMap = new HashMap<>();
        for (MymContractDto contract : contractList){
            //房间签约回款场景及场景对应的房源第N个月回款金额计算map(key；24月，value：每月回款金额)
            Map<Integer, BigDecimal> amountMap = MyMapUtils.getMoneyByMonthMap();

            String contractGUID = contract.getContractGUID();
            //签约时间
            LocalDateTime ywgsDate = contract.getYwgsDate();
            //签约成交总价
            BigDecimal cjRmbTotal = contract.getCjRmbTotal();
            //补差金额
            BigDecimal BcTotal = contract.getAuditDate() != null ? contract.getBcTotal() : BigDecimal.ZERO;

            //退房合同金额为0
            if(contract.getStatus().equals(ActivationEnum.CLOSE.getKey()) && LocalDateTimeUtils.compareDate(contract.getCloseDate(), LocalDateTime.now(), 0)){
                cjRmbTotal = BigDecimal.ZERO;
            }
            //补差审核日期
            LocalDateTime auditDate = contract.getAuditDate();
            //补差金额
            BigDecimal bcTotal = contract.getBcTotal();

            //获取合同应收金额
            BigDecimal receivableAmountTotal = mySaleDataBO.getReceivableAmountByContractGUID(contractGUID);
            //获取合同实收金额
            BigDecimal receiptsAmountTotal = mySaleDataBO.getReceiptsAmountByContractGUID(contractGUID);
            //脏数据（应收总额与合同金额(含补差)差绝对值大于100）
            if (receivableAmountTotal.subtract(cjRmbTotal.add(BcTotal)).abs().intValue() > tolerance.intValue()) {
                continue;
            }

            //获取合同实收金额Map
            Map<String, BigDecimal> contractReceiptsAmountMap = mySaleDataBO.getContractReceiptsAmountMapByContractGUID(contractGUID);
            if(CollectionUtils.isNotEmpty(contractReceiptsAmountMap)){
                //历史月已实收款计算回款金额
                for (Entry<String, BigDecimal> entry : contractReceiptsAmountMap.entrySet()){
                    int diff = LocalDateTimeUtils.monthDiff(ywgsDate, LocalDateTimeUtils.toLocalDateTime(entry.getKey()));
                    if (diff <= 0){
                        amountMap.put(1,amountMap.get(1).add(entry.getValue()));
                    } else if (diff >= 24){
                        amountMap.put(24,amountMap.get(24).add(entry.getValue()));
                    } else {
                        amountMap.put(diff+1,amountMap.get(diff+1).add(entry.getValue()));
                    }
                }
            }
            /**
             * 1、合同金额 = 实收金额(包含容差)
             * 2、合同金额+ 补差金额 = 实收金额(包含容差)
             * 以实收款项计算房源第N个月的回款金额
             */
            if (auditDate != null && bcTotal.compareTo(BigDecimal.ZERO) != 0 && receiptsAmountTotal.subtract(cjRmbTotal.add(bcTotal)).abs().compareTo(tolerance) <= 0 ) {
                //补差金额添加到最后一笔实收款所属月份,若无实收款取当前月;
                String maxTime;
                if(CollectionUtils.isNotEmpty(contractReceiptsAmountMap)){
                    maxTime = contractReceiptsAmountMap.entrySet().stream().map(s->s.getKey()).max(Comparator.comparing(s->s)).orElse(currentYearMonth);
                }else {
                    maxTime = currentYearMonth;
                }
                //获取差异月数
                int diff = LocalDateTimeUtils.monthDiff(ywgsDate, LocalDateTimeUtils.toLocalDateTime(maxTime));
                if (diff <= 0){
                    amountMap.put(1,amountMap.get(1).add(bcTotal));
                } else if (diff >= 24){
                    amountMap.put(24,amountMap.get(24).add(bcTotal));
                } else {
                    amountMap.put(diff+1,amountMap.get(diff+1).add(bcTotal));
                }
            }else
            /**
             * 3、房源已收回全款，但回款金额超过签约金额
             * 超收金额算到（当前年月）的回款退款（负的超收金额）
             */
                if (receiptsAmountTotal.subtract(cjRmbTotal).compareTo(tolerance)> 0) {
                    BigDecimal refundAmount = cjRmbTotal.subtract(receiptsAmountTotal);
                    int diff = LocalDateTimeUtils.monthDiff(ywgsDate,LocalDateTime.now());
                    if (diff <= 0){
                        amountMap.put(1,amountMap.get(1).add(refundAmount));
                    } else if (diff >= 24){
                        amountMap.put(24,amountMap.get(24).add(refundAmount));
                    } else {
                        amountMap.put(diff+1,amountMap.get(diff+1).add(refundAmount));
                    }
                }else
                /**
                 * 4、房源未收回全款
                 * (1)、当月之前实收等于当前之前应收：未收回金额按照应收单计算
                 * (2)、当月之前实收小于当前之前应收：将小于的金额放在当月计算
                 * (3)、当月之前实收大于当前之前应收：将大于的金额从当月及之后的应收单中顺延计算
                 */
                    if (cjRmbTotal.subtract(receiptsAmountTotal).compareTo(tolerance) > 0) {
                        //获取合同实收金额Map
                        Map<String, BigDecimal> contractReceivableAmountMap = mySaleDataBO.getContractReceiptsAmountMapByContractGUIDGeYearMonth(contractGUID,currentYearMonth);

                        //获取小于指定年月的合同应收金额
                        BigDecimal leCurrentReceivableAmountTotal = mySaleDataBO.getReceivableAmountByContractGUIDLeYearMonth(contractGUID,currentYearMonth);
                        BigDecimal differenceAmount = leCurrentReceivableAmountTotal.subtract(receiptsAmountTotal);
                        if(differenceAmount.compareTo(BigDecimal.ZERO) > 0){
                            BigDecimal receivableAmount = contractReceivableAmountMap.get(currentYearMonth);
                            if(null != receivableAmount){
                                contractReceivableAmountMap.put(currentYearMonth,differenceAmount.add(receiptsAmountTotal));
                            }else {
                                contractReceivableAmountMap.put(currentYearMonth,differenceAmount);
                            }
                        }else if(differenceAmount.compareTo(BigDecimal.ZERO) < 0){
                            BigDecimal differenceReceivableAmount = differenceAmount;
                            List<String> dateTimeList = contractReceivableAmountMap.keySet().stream().sorted(Comparator.comparing(s -> s)).collect(Collectors.toList());
                            for(String dateTime : dateTimeList){
                                BigDecimal amount = contractReceivableAmountMap.get(dateTime);
                                differenceReceivableAmount = differenceReceivableAmount.add(amount);
                                if(differenceReceivableAmount.compareTo(BigDecimal.ZERO) >= 0){
                                    contractReceivableAmountMap.put(dateTime,differenceReceivableAmount);
                                    break;
                                }else {
                                    contractReceivableAmountMap.put(dateTime,BigDecimal.ZERO);
                                }
                            }
                        }
                        for (Entry<String, BigDecimal> entry : contractReceivableAmountMap.entrySet()){
                            int diff = LocalDateTimeUtils.monthDiff(ywgsDate, LocalDateTimeUtils.toLocalDateTime(entry.getKey()));
                            if (diff <= 0){
                                amountMap.put(1,amountMap.get(1).add(entry.getValue()));
                            } else if (diff >= 24){
                                amountMap.put(24,amountMap.get(24).add(entry.getValue()));
                            } else {
                                amountMap.put(diff+1,amountMap.get(diff+1).add(entry.getValue()));
                            }
                        }
                    }
            contractAmountMap.put(contractGUID,amountMap);
        }
        return contractAmountMap;
    }

    /**
     * 获取项目销售取数系统
     * @param projectId
     * @return
     */
    @Override
    public String getProjectSaleFetchSys(String projectId){
        List<SysProjectFetchNumber> fetchNumberList = projectFetchNumberService.selectByProjectId(projectId);
        if(CollectionUtils.isEmpty(fetchNumberList)){
            throw new MsgException(CodeEnum.SYS_PROJECT_FETCH_ERROR);
        }
        Set<String> countingSystemIdList = fetchNumberList.stream().map(s -> s.getCountingSystemId()).collect(Collectors.toSet());
        if(CollectionUtils.isEmpty(countingSystemIdList) || countingSystemIdList.size() > 1){
            throw new MsgException(CodeEnum.CREATE_COEFFICIENT_PROJECT_FETCH_NON_UNIQUENESS_ERROR);
        }
        return new ArrayList<>(countingSystemIdList).get(0);
    }



    /**
     * 新建实际版回款系数详情表
     *
     * @param coefficientId         项目id
     * @param standardFormatsReturn 业态第N个月的回款系数
     * @param suiteRateMap          套数去化率
     * @param effectiveDates        生效日期
     */
    @Override
    public void saveRealityDetail(String coefficientId, Map<String, Map<Integer, BigDecimal>> standardFormatsReturn, Map<String, BigDecimal> suiteRateMap, Map<String, LocalDateTime> effectiveDates, Map<String, BigDecimal> signingRateMap, Map<String, LocalDateTime> signFirstTimeMap) {
        if(CollectionUtils.isEmpty(standardFormatsReturn)){
            throw new MsgException(CodeEnum.CREATE_COEFFICIENT_CONTRACT_IS_NULL_ERROR);
        }

        ReturnCoefficientDetail coefficientDetail = new ReturnCoefficientDetail();
        //standardFormatsReturn(key:业态编码,value(key:月份，value：系数))
        Set<String> standardCodes = standardFormatsReturn.keySet();
        for (String standardCode : standardCodes) {
            Map<Integer, BigDecimal> map = standardFormatsReturn.get(standardCode);
            //回款系数详情表id
            coefficientDetail.setId(UUIDUtils.create());
            //回款系数版本表id
            coefficientDetail.setCoefficientId(coefficientId);
            //创建人员
            coefficientDetail.setCreateBy(Constants.SYS);
            //创建日期
            coefficientDetail.setCreateTime(LocalDateTime.now());
            //是否删除
            coefficientDetail.setIsDelete(IsDeleteEnum.NO.getKey());
            //签约回款率
            coefficientDetail.setSigningRate(signingRateMap.get(standardCode));
            //首次签约日期
            coefficientDetail.setDateInitialSigning(signFirstTimeMap.get(standardCode));
            //套数去化率
            coefficientDetail.setSuiteSellingRate(suiteRateMap.get(standardCode));
            //生效日期
            coefficientDetail.setEffectiveDate(effectiveDates.get(standardCode));
            //业态编码
            coefficientDetail.setProProductCode(standardCode);
            coefficientDetail.setProProductType(StandardFormatsEnum.getValueByKey(standardCode));
            //三大业态编码
            String code = StandardFormatsMapEnum.getValueByKey(standardCode);
            coefficientDetail.setThreeProductCode(code);
            //三大业态type
            coefficientDetail.setThreeProductName(StandardFormatsEnum.getValueByKey(code));
            //每月回款系数
            coefficientDetail.setOneMonth(map.get(1));
            coefficientDetail.setTwoMonth(map.get(2));
            coefficientDetail.setThreeMonth(map.get(3));
            coefficientDetail.setFourMonth(map.get(4));
            coefficientDetail.setFiveMonth(map.get(5));
            coefficientDetail.setSixMonth(map.get(6));
            coefficientDetail.setSevenMonth(map.get(7));
            coefficientDetail.setEightMonth(map.get(8));
            coefficientDetail.setNineMonth(map.get(9));
            coefficientDetail.setTenMonth(map.get(10));
            coefficientDetail.setElevenMonth(map.get(11));
            coefficientDetail.setTwelveMonth(map.get(12));
            coefficientDetail.setThirteenMonth(map.get(13));
            coefficientDetail.setFourteenMonth(map.get(14));
            coefficientDetail.setFifteenMonth(map.get(15));
            coefficientDetail.setSixteenMonth(map.get(16));
            coefficientDetail.setSeventeenMonth(map.get(17));
            coefficientDetail.setEighteenMonth(map.get(18));
            coefficientDetail.setNineteenMonth(map.get(19));
            coefficientDetail.setTwentyMonth(map.get(20));
            coefficientDetail.setTwentyOneMonth(map.get(21));
            coefficientDetail.setTwentyTwoMonth(map.get(22));
            coefficientDetail.setTwentyThreeMonth(map.get(23));
            coefficientDetail.setTwentyFourMonth(map.get(24));
            //初始化第24月系数
            coefficientDetail.initializeTwentyFourMonth();
            this.save(coefficientDetail);
        }
    }
    
    /**
     * 获取标准业态下全部子类code集合
     */
    private Map<String, List<String>> getStandardCodeMap(List<String> singIdList) {
    	List<Map<String, Object>> list = returnCoefficientDetailWrapper.queryProductCodeBySingIdList(singIdList);
    	//map(key:标准业态编码,value：子类编码集合)
        Map<String, List<String>> resMap = new HashMap<>();
    	for(Map<String, Object> map : list) {
    		resMap.put(String.valueOf(map.get("parentCode")), Arrays.asList(String.valueOf(map.get("childCode")).split(",")));
    	}
        return resMap;
    }

    /**
     * 获取最后签约日期
     *
     * @param singIdList
     * @return
     */
    private Map<String, LocalDateTime> getLateFinishDate(List<String> singIdList) {


        Map<String, LocalDateTime> lateFinishDateMap = new HashMap<>();
        //map(key:标准业态：value：id集合List<String>)
        Map<String, List<String>> pkIdMap = new HashMap<>();

        // 2.1首先通过签约版本id列表 查询 t_sign_plan_version 签约版本表 中的货值版本id 列表

        // 2.2通过货值版本id列表 查询t_price_version 货值版本表中的 楼栋计划版本id 列表
        // 2.3通过楼栋计划版本id列表 查询t_panoplan_build_plan_item 楼栋版本计划楼栋表 中的id 列表

        Map<String, List<String>> standardCodeMap = getStandardCodeMap(singIdList);
        Set<String> codes = standardCodeMap.keySet();
        List<PanoplanBuildPlanItem> buildPlanList = returnCoefficientDetailWrapper.queryBySingIdList(singIdList);

        for (String code : codes) {

            ArrayList<String> list = new ArrayList<>();
            for (PanoplanBuildPlanItem panoplanBuildPlanItem : buildPlanList) {

                String productTypeNum = panoplanBuildPlanItem.getProductTypeNum();
                String id = panoplanBuildPlanItem.getId();


                List<String> childCodes = standardCodeMap.get(code);
                for (String childCode : childCodes) {
                    if (productTypeNum.equals(childCode)) {
                        list.add(id);
                    }
                }
            }
            pkIdMap.put(code, list);
        }

        // 2.4 根据 id集合 对应 t_panoplan_build_plan_item_detail  楼栋版本计划楼栋详情表
        Set<String> codeSet = pkIdMap.keySet();
        for (String code : codeSet) {
            LocalDateTime lateFinishDate = LocalDateTime.MIN;
            List<String> pkIdsList = pkIdMap.get(code);

            List<PanoplanBuildPlanItemDetail> buildPlanItemDetails = returnCoefficientDetailWrapper.queryByPkIdList(pkIdsList);
            for (PanoplanBuildPlanItemDetail buildPlanItemDetail : buildPlanItemDetails) {
                //计划完成时间
                LocalDateTime plannedFinishDate = buildPlanItemDetail.getPlannedFinishDate();
                //实际完成时间
                LocalDateTime actualFinishDate = buildPlanItemDetail.getActualFinishDate();
                if (null == actualFinishDate) {
                    if (plannedFinishDate == null) {
                        continue;
                    }
                    if (lateFinishDate.isBefore(plannedFinishDate)) {
                        lateFinishDate = plannedFinishDate;
                    }
                } else {
                    if (lateFinishDate.isBefore(actualFinishDate)) {
                        lateFinishDate = actualFinishDate;
                    }
                }
            }
            lateFinishDateMap.put(code, lateFinishDate);
        }

        return lateFinishDateMap;
    }

    /**
     * 实际版回款系数生效日期map（key:标准业态，value：生效日期）
     * 1、去化率大于等于10%且销售时间满6个月 -> 生效日期为首次签约日期+7个月
     * 2、去化率大于等于30% -> 生效日期为回款版本首次计算月
     * @param signFirstTime 首次生效日期
     * @param productSoldNumRateMap  套数去化率
     * @param formerProjectDetailList
     * @return
     */
    private Map<String, LocalDateTime> getEffectiveDatesMap(Map<String, LocalDateTime> signFirstTime, Map<String, BigDecimal> productSoldNumRateMap, List<ReturnCoefficientDetail> formerProjectDetailList) {
        Map<String, BigDecimal> suiteSellingRateMap = new HashMap<>();
        Map<String, LocalDateTime> fromerEffectiveDateMap = new HashMap<>();
        if(CollectionUtils.isNotEmpty(formerProjectDetailList)){
            suiteSellingRateMap = formerProjectDetailList.stream().collect(Collectors.toMap(s -> s.getProProductCode(), s -> s.getSuiteSellingRate()));
            fromerEffectiveDateMap = formerProjectDetailList.stream()
                    .filter(s->s.getEffectiveDate() != null ).collect(Collectors.toMap(s -> s.getProProductCode(), s -> s.getEffectiveDate()));
        }

        Map<String, LocalDateTime> effectiveDateMap = new HashMap<>();
        for (Entry<String, LocalDateTime> timeEntry : signFirstTime.entrySet()){
            LocalDateTime effectiveDate = null;
            BigDecimal rate = productSoldNumRateMap.get(timeEntry.getKey());

            int diff = LocalDateTimeUtils.monthDiff_two(timeEntry.getValue(),LocalDateTime.now());
            if(diff >= 6  && rate.doubleValue() >= 0.1){
                effectiveDate = timeEntry.getValue().plusMonths(6);
            }else if(rate.doubleValue() >= 0.3) {
                if(CollectionUtils.isEmpty(suiteSellingRateMap)){
                    effectiveDate = LocalDateTime.now();
                }else {
                    if(suiteSellingRateMap.get(timeEntry.getKey()).doubleValue() >= 0.3){
                        effectiveDate = fromerEffectiveDateMap.get(timeEntry.getKey());
                    }else {
                        effectiveDate = LocalDateTime.now();
                    }
                }
            }
            effectiveDateMap.put(timeEntry.getKey(), effectiveDate);
        }
        return effectiveDateMap;
    }


    /**
     * 判断实体类是否为空 true 不为空  false 为空
     */
    public boolean checkObjFieldIsNotNull(Object obj) {   
        boolean flag = false;
        try {
            for (Field f : obj.getClass().getDeclaredFields()) {
                f.setAccessible(true);
                if (f.get(obj) == null || f.get(obj) == "") {
                } else {
                    flag = true;
                }
            }
        } catch (Exception e) {
            return false;
        }
        return flag;
    }
}

